use std::ops::Deref;

use bytes::{Buf, BytesMut};

use crate::{RespDecode, RespEncode, RespError, RespFrame};

use super::{calc_total_length, parse_length, BUF_CAP, CRLF_LEN};

impl RespDecode for RespSet {
    const PREFIX: &'static str = "~";

    fn decode(buf: &mut BytesMut) -> Result<Self, RespError> {
        let (end, len) = parse_length(buf, Self::PREFIX)?;

        let total_len = calc_total_length(buf, end, len, Self::PREFIX)?;

        if buf.len() < total_len {
            return Err(RespError::NotComplete);
        }

        buf.advance(end + CRLF_LEN);

        let mut frames = Vec::new();
        for _ in 0..len {
            frames.push(RespFrame::decode(buf)?);
        }

        Ok(RespSet::new(frames))
    }

    fn expect_length(buf: &[u8]) -> Result<usize, RespError> {
        let (end, len) = parse_length(buf, Self::PREFIX)?;
        calc_total_length(buf, end, len, Self::PREFIX)
    }
}

// set: "~<number-of-elements>\r\n<element-1>...<element-n>"
// 先为set的类型实现 encode 方法
impl RespEncode for RespSet {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(BUF_CAP);
        let len = self.0.len();
        let first_value = format!("~{}\r\n", len);
        buf.extend_from_slice(&first_value.into_bytes());
        for ele in self.0 {
            buf.extend_from_slice(&ele.encode());
        }
        buf
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct RespSet(pub(crate) Vec<RespFrame>);

impl Deref for RespSet {
    type Target = Vec<RespFrame>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl RespSet {
    #[allow(dead_code)]
    pub fn new(s: impl Into<Vec<RespFrame>>) -> Self {
        Self(s.into())
    }
}

#[cfg(test)]
mod tests {
    use crate::SimpleString;

    use super::*;
    use bytes::BytesMut;

    use anyhow::Result;

    #[test]
    fn test_set_decode() -> Result<()> {
        let mut buf = BytesMut::new();
        buf.extend_from_slice(b"~2\r\n+hello\r\n+world\r\n");
        let frame = RespSet::decode(&mut buf)?;
        let v = vec![
            SimpleString::new("hello").into(),
            SimpleString::new("world").into(),
        ];
        let ret = RespSet::new(v);
        assert_eq!(frame, ret);
        Ok(())
    }

    // "~<number-of-entries>\r\n<element-1>...<element-n>"
    #[test]
    fn test_set_encode() {
        let val = vec![
            SimpleString::new("hello").into(),
            SimpleString::new("world").into(),
        ];
        let set = RespSet::new(val);
        let buf = set.encode();
        assert_eq!(buf, b"~2\r\n+hello\r\n+world\r\n");
    }
}
